home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / tde3.zip / DIRLIST.C < prev    next >
C/C++ Source or Header  |  1993-06-05  |  31KB  |  946 lines

  1. /*
  2.  * I wrote this function because I'm so stupid, I constantly forget
  3.  *  file names and directory stuff.  The main function prompts for a
  4.  *  subdirectory name or a search path.  The default search path is the
  5.  *  cwd (current working directory).  In addition to being stupid, I'm also
  6.  *  lazy.  If the user types a subdirectory name, I think we can assume he
  7.  *  wants to list all files w/o having to type *.*   Let's save the cwd on
  8.  *  whatever drive the user wishes to search, so we can restore it we get
  9.  *  thru dir'ing.  Use the standard DOS functions to get and set directories.
  10.  *
  11.  * The search pattern can contain wild card chars, valid file names, or a
  12.  *  valid subdirectory name.
  13.  *
  14.  * Being that TDE 2.2 now handles binary files, lets make .EXE and .COM files
  15.  *  autoload in binary mode.
  16.  *
  17.  * Before matching files are displayed on the screen, file names are sorted
  18.  *  using the easy-to-implement and fairly fast Shellsort algorithm.
  19.  *
  20.  * See:
  21.  *
  22.  *   Donald Lewis Shell, _Communications of the ACM_ 2 (No. 2): 30-32, 1959.
  23.  *
  24.  * See also:
  25.  *
  26.  *   Donald Ervin Knuth, _The Art of Computer Programming; Volume 3:  Sorting
  27.  *     and Searching_, Addison-Wesley, Reading, Mass., 1973, Chapter 5,
  28.  *     "Sorting", pp 84-95.  ISBN 0-201-03803-X.
  29.  *
  30.  *   Robert Sedgewick, _Algorithms in C_, Addison-Wesley, Reading, Mass.,
  31.  *     1990, Chapter 8, "Elementary Sorting Methods", pp 107-111.
  32.  *     ISBN 0-201-51425-7.
  33.  *
  34.  *
  35.  * New editor name:  TDE, the Thomson-Davis Editor.
  36.  * Author:           Frank Davis
  37.  * Date:             June 5, 1991, version 1.0
  38.  * Date:             July 29, 1991, version 1.1
  39.  * Date:             October 5, 1991, version 1.2
  40.  * Date:             January 20, 1992, version 1.3
  41.  * Date:             February 17, 1992, version 1.4
  42.  * Date:             April 1, 1992, version 1.5
  43.  * Date:             June 5, 1992, version 2.0
  44.  * Date:             October 31, 1992, version 2.1
  45.  * Date:             April 1, 1993, version 2.2
  46.  * Date:             June 5, 1993, version 3.0
  47.  *
  48.  * This code is released into the public domain, Frank Davis.
  49.  *    You may distribute it freely.
  50.  */
  51.  
  52. #include "tdestr.h"
  53. #include "common.h"
  54. #include "define.h"
  55. #include "tdefunc.h"
  56.  
  57.  
  58. /*
  59.  * Name:    dir_help
  60.  * Purpose: To prompt the user and list the directory contents
  61.  * Date:    February 13, 1992
  62.  * Passed:  window:  pointer to current window
  63.  */
  64. int  dir_help( WINDOW *window )
  65. {
  66. char dname[MAX_COLS+2]; /* directory search pattern */
  67. char stem[MAX_COLS+2];  /* directory stem */
  68. char drive[_MAX_DRIVE]; /* splitpath drive buff */
  69. char dir[_MAX_DIR];     /* splitpath dir buff */
  70. char fname[_MAX_FNAME]; /* splitpath fname buff */
  71. char ext[_MAX_EXT];     /* splitpath ext buff */
  72. int  rc;
  73. int  file_mode;
  74. int  bin_length;
  75. int  prompt_line;
  76.  
  77.    if (window != NULL) {
  78.       entab_linebuff( );
  79.       if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  80.          return( ERROR );
  81.       prompt_line = window->bottom_line;
  82.    } else
  83.       prompt_line = g_display.nlines;
  84.  
  85.    /*
  86.     * search path or pattern
  87.     */
  88.    dname[0] = '\0';
  89.    rc = get_name( dir1, prompt_line, dname, g_display.message_color );
  90.  
  91.    if (rc == OK) {
  92.       if (validate_path( dname, stem ) == OK) {
  93.          rc = list_and_pick( dname, stem, window );
  94.  
  95.          /*
  96.           * if everything is everything, load in the file selected by user.
  97.           */
  98.          if (rc == OK) {
  99.             file_mode = TEXT;
  100.             bin_length = 0;
  101.             _splitpath( dname, drive, dir, fname, ext );
  102.             if (stricmp( ext, ".exe" ) == 0  ||  stricmp( ext, ".com" ) == 0) {
  103.                file_mode = BINARY;
  104.                bin_length = g_status.file_chunk;
  105.             }
  106.             if (window != NULL)
  107.                attempt_edit_display( dname, LOCAL, file_mode, bin_length );
  108.             else
  109.                attempt_edit_display( dname, GLOBAL, file_mode, bin_length );
  110.          }
  111.       } else
  112.          /*
  113.           * invalid path or file name
  114.           */
  115.          error( WARNING,
  116.                 window != NULL ? window->bottom_line : g_display.nlines, dir2 );
  117.    }
  118.    return( rc );
  119. }
  120.  
  121.  
  122. /*
  123.  * Name:    validate_path
  124.  * Purpose: make sure we got a valid search pattern or subdirectory
  125.  * Date:    February 13, 1992
  126.  * Passed:  dname: search path entered by user
  127.  *          stem:  directory stem is returned
  128.  * Returns: successful or not
  129.  * Notes:   we need to validate the search path or pattern.  if the search
  130.  *            pattern is valid, then we need to get the search stem.
  131.  *          the user may enter a subdirectory or some kind of search pattern.
  132.  *             if the user enters a subdirectory, then there are a few things
  133.  *             we need to take care of  1) find out if the subdirectory is
  134.  *             the root, 2) append a '\' to the subdirectory so we can create
  135.  *             a search pattern for the subdirectory, 3) don't append '\' to
  136.  *             the root, it already has a '\'.
  137.  *          if the user enters a search pattern, then we need to dissect the
  138.  *             search path.  we must create a stem from the search pattern.
  139.  */
  140. int  validate_path( char *dname, char *stem )
  141. {
  142. int  rc;
  143. DTA  dta;               /* temp disk transfer struct for findfirst, etc. */
  144. int  fattr;
  145. int  i;
  146. int  len;
  147. char *p;
  148. char temp[MAX_COLS+2];  /* directory stem */
  149.  
  150.    /*
  151.     * if path name is void then the current working directory is implied.
  152.     */
  153.    if (dname[0] == '\0') {
  154.  
  155.       assert( strlen( stardotstar ) < MAX_COLS );
  156.  
  157.       strcpy( dname, stardotstar );
  158.       stem[0] = '\0';
  159.       rc = OK;
  160.    } else {
  161.  
  162.       /*
  163.        * get the attributes of the search pattern, so we can determine if
  164.        * this is a pattern or subdirectory.
  165.        */
  166.       rc = get_fattr( dname, &fattr );
  167.  
  168.       if (rc == OK && (fattr & SUBDIRECTORY)) {
  169.          assert( strlen( dname ) < MAX_COLS );
  170.          strcpy( stem, dname );
  171.  
  172.          /*
  173.           * if this is the root directory ( \ ), don't append '\' to it.
  174.           * user entered a subdirectory - append *.* to get contents of
  175.           * subdirectory.
  176.           */
  177.          len = strlen( stem );
  178.          if (stem[len-1] != '\\') {
  179.             strcat( stem, "\\" );
  180.             strcat( dname, "\\" );
  181.          }
  182.          strcat( dname, stardotstar );
  183.  
  184.       /*
  185.        * not a subdirectory.  let's see if any files match the search
  186.        * pattern.
  187.        */
  188.       } else if (rc != ERROR) {
  189.          if ((rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  190.                               SYSTEM | SUBDIRECTORY | ARCHIVE )) == OK) {
  191.  
  192.             /*
  193.              * copy dname to "temp" so we can use "temp" to find the stem.
  194.              *    we need to search the pattern backwards to figure the stem.
  195.              */
  196.  
  197.             assert( strlen( dname ) < MAX_COLS );
  198.  
  199.             strcpy( temp, dname );
  200.             len = strlen( dname );
  201.             for (i=len,p=temp+len; i>=0; i--) {
  202.                /*
  203.                 *  if we run into the '\' or the ':', then we got a stem.
  204.                 */
  205.                if (*p == '\\' || *p == ':') {
  206.                   p = temp + i;
  207.                   *(p+1) = '\0';
  208.                   break;
  209.                /*
  210.                 * if we're at the beginning of the string, stem == '\0'
  211.                 */
  212.                } else if (i == 0) {
  213.                   *p = '\0';
  214.                   break;
  215.                }
  216.                --p;
  217.             }
  218.  
  219.             assert( strlen( temp ) < MAX_COLS );
  220.  
  221.             strcpy( stem, temp );
  222.          } else
  223.             rc = ERROR;
  224.  
  225.       /*
  226.        * user did not enter a valid subdirectory name or search pattern.
  227.        */
  228.       } else
  229.          rc = ERROR;
  230.    }
  231.    return( rc );
  232. }
  233.  
  234.  
  235. /*
  236.  * Name:    list_and_pick
  237.  * Purpose: To show matching file names and let user pick a file
  238.  * Date:    February 13, 1992
  239.  * Passed:  dname:  directory search pattern
  240.  *          stem:   stem of directory search pattern
  241.  *          window:  pointer to current window
  242.  * Returns: return code from pick.  rc = OK, then edit a new file.
  243.  * Notes:   real work routine of this function.  save the cwd and let the
  244.  *           user search upwards or downwards thru the directory structure.
  245.  *          since we are doing DOS directory functions, we need to check the
  246.  *           return code after each DOS call for critical errors.
  247.  */
  248. int  list_and_pick( char *dname, char *stem, WINDOW *window )
  249. {
  250. int  rc;
  251. DTA  dta;               /* disk transfer address for findfirst */
  252. DIRECTORY dir;          /* contains all info for dir display */
  253. unsigned int cnt;       /* number of matching files */
  254. FTYPE *flist, *p;       /* pointer to list of matching files */
  255. char cwd[MAX_COLS];     /* save the current working directory in this buff */
  256. char dbuff[MAX_COLS];   /* temporary directory buff */
  257. char prefix[MAX_COLS];  /* directory prefix  */
  258. int  change_directory = FALSE;
  259. int  stop;
  260. int  len;
  261. int  drive;
  262.  
  263.    /*
  264.     * Some algorithms alloc the maximum possible number of files in
  265.     *  a directory, eg. 256 or 512.  Let's count the number of matching
  266.     *  files so we know egxactly how much memory to request from calloc.
  267.     *  Depending on the op system, disk media, disk format, or version of DOS,
  268.     *  the max number of files may vary, anyway, also, additionally.
  269.     */
  270.    rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  271.                                 SUBDIRECTORY | ARCHIVE );
  272.    if (rc != ERROR) {
  273.       for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  274.          ++cnt;
  275.       flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  276.    } else
  277.       flist = NULL;
  278.    if (rc != ERROR && flist != NULL) {
  279.  
  280.       stop = FALSE;
  281.       /*
  282.        * If user entered drive name in search pattern, find out the drive and
  283.        *  directory stem.
  284.        */
  285.       if (stem[1] == ':') {
  286.  
  287.          /*
  288.           * If the second character of the search pattern is a ':', the
  289.           *  the first character of the pattern should be the drive.
  290.           *  Convert drive to lower case and get a numerical representation.
  291.           * CAVEAT:  In DOS v 2.x, there may be up to 63 logical drives.
  292.           *   my algorithm may blow up if the number of logical drives
  293.           *   is greater than 'Z'.
  294.           * For DOS >= 3, the number of drives is limited to 26, I think.
  295.           */
  296.          drive = stem[0];
  297.          if (drive < 'a')
  298.             drive += 32;
  299.          drive = drive - 'a' + 1;
  300.          rc = get_current_directory( dbuff, drive );
  301.          if (rc == ERROR)
  302.             stop = TRUE;
  303.          else {
  304.  
  305.             /*
  306.              * Put drive letter, ':', and '\' in front of current directory.
  307.              */
  308.             prefix[0] = (char)(drive - 1 + 'a');
  309.             prefix[1] = ':';
  310.             prefix[2] = '\\';
  311.             prefix[3] = '\0';
  312.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  313.             strcpy( cwd, prefix );
  314.             strcat( cwd, dbuff );
  315.          }
  316.  
  317.       /*
  318.        * else get current directory from default drive
  319.        */
  320.       } else {
  321.  
  322.          /*
  323.           * 0 = default drive.
  324.           */
  325.          drive = 0;
  326.          rc = get_current_directory( dbuff, drive );
  327.          if (rc == ERROR)
  328.             stop = TRUE;
  329.          else {
  330.  
  331.             /*
  332.              * Put a '\' in front of the current directory.
  333.              */
  334.             prefix[0] = '\\';
  335.             prefix[1] = '\0';
  336.  
  337.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  338.  
  339.             strcpy( cwd, prefix );
  340.             strcat( cwd, dbuff );
  341.          }
  342.       }
  343.  
  344.       while (stop == FALSE) {
  345.          /*
  346.           * If we had enough memory, find all matching file names.  Append
  347.           *  '\\' at the end of subdirectory names so user will know if
  348.           *  name is a directory.  Might as well find everything, because
  349.           *  i also forget subdirectory names, too.
  350.           *
  351.           * when we get here, we have already done: 1) my_findfirst and
  352.           *  my_findnext, 2) counted the number of matching files, and
  353.           *  3) allocated space.
  354.           */
  355.          p = flist;
  356.          cnt = 0;
  357.  
  358.          rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  359.                                  SUBDIRECTORY | ARCHIVE );
  360.          if (rc != ERROR) {
  361.  
  362.             /*
  363.              * p is pointer that walks down the file info structure.
  364.              *  save the file name, file size, and directory character,
  365.              *  if needed, for each matching file we find.
  366.              */
  367.  
  368.             assert( strlen( dta.name ) < 14 );
  369.  
  370.             strcpy( p->fname, dta.name );
  371.             p->fsize = dta.size;
  372.             if (dta.attrib & SUBDIRECTORY)
  373.                strcat( p->fname, "\\" );
  374.             for (cnt=1; (rc = my_findnext( &dta )) == OK; ) {
  375.                ++p;
  376.  
  377.                assert( strlen( dta.name ) < 14 );
  378.  
  379.                strcpy( p->fname, dta.name );
  380.                p->fsize = dta.size;
  381.                if (dta.attrib & SUBDIRECTORY)
  382.                   strcat( p->fname, "\\" );
  383.                cnt++;
  384.             }
  385.          }
  386.  
  387.          if (rc != ERROR) {
  388.             shell_sort( flist, cnt );
  389.  
  390.             /*
  391.              * figure out number of rows, cols, etc... then display dir list
  392.              */
  393.             setup_directory_window( &dir, cnt );
  394.             write_directory_list( flist, dir );
  395.  
  396.             /*
  397.              * Let user select file name or another search directory.
  398.              *  Save the choice in dbuff.  rc == OK if user selected file or dir.
  399.              */
  400.             rc = select_file( flist, stem, &dir );
  401.  
  402.             assert( strlen( flist[dir.select].fname ) < MAX_COLS );
  403.  
  404.             strcpy( dbuff, flist[dir.select].fname );
  405.          }
  406.  
  407.          /*
  408.           *  give memory back.
  409.           */
  410.          free( flist );
  411.  
  412.          if (rc == ERROR)
  413.             stop = TRUE;
  414.          else {
  415.             len = strlen( dbuff );
  416.  
  417.             /*
  418.              * If the last character in a file name is '\' then let's
  419.              *  do a dir on selected directory.  See the matching
  420.              *  else when the user selects a file.
  421.              */
  422.             if (dbuff[len-1] == '\\') {
  423.  
  424.                /*
  425.                 * Stem has subdirectory path.  dbuff has selected path.
  426.                 * Create a new dname with stem and dbuff.
  427.                 */
  428.  
  429.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  430.  
  431.                strcpy( dname, stem );
  432.                strcat( dname, dbuff );
  433.                len = strlen( dname );
  434.                strcpy( dbuff, dname );
  435.  
  436.                /*
  437.                 * The last character in dbuff is '\', because we append the
  438.                 *  '\' to every directory entry in the file list.  Replace
  439.                 *  it with a NULL char then we will have a valid path name.
  440.                 */
  441.                dbuff[len-1] = '\0';
  442.  
  443.                /*
  444.                 * now let's change to the selected subdirectory.
  445.                 */
  446.                rc = set_current_directory( dbuff );
  447.                if (rc == OK) {
  448.  
  449.                   /*
  450.                    * Every time we change directories, we need to get the
  451.                    *  current directory so we will be sure to have the
  452.                    *  correct path.
  453.                    */
  454.                   rc = get_current_directory( dbuff, drive );
  455.                   if (rc == OK) {
  456.  
  457.                      assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  458.  
  459.                      strcpy( dname, prefix );
  460.                      strcat( dname, dbuff );
  461.                      change_directory = TRUE;
  462.                   }
  463.                }
  464.  
  465.                /*
  466.                 * Validate the new path and allocate memory for the
  467.                 *  matching files.
  468.                 */
  469.                if (rc == OK)
  470.                   rc = validate_path( dname, stem );
  471.                if (rc == OK) {
  472.                   rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  473.                                   SYSTEM | SUBDIRECTORY | ARCHIVE );
  474.                   if (rc != ERROR) {
  475.                      for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  476.                         ++cnt;
  477.                      flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  478.                   }
  479.                }
  480.                if (flist == NULL || rc == ERROR) {
  481.                   stop = TRUE;
  482.                   rc = ERROR;
  483.                }
  484.             } else {
  485.  
  486.                /*
  487.                 * user selected a file.  store fname in dname and return.
  488.                 */
  489.                rc = OK;
  490.                stop = TRUE;
  491.  
  492.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  493.  
  494.                strcpy( dname, stem );
  495.                strcat( dname, dbuff );
  496.             }
  497.          }
  498.       }
  499.  
  500.       /*
  501.        * Go back to the current directory if needed.
  502.        */
  503.       if (change_directory)
  504.          set_current_directory( cwd );
  505.       if (window != NULL)
  506.          redraw_screen( window );
  507.    } else {
  508.       /*
  509.        * out of memory
  510.        */
  511.       error( WARNING,  window != NULL ? window->bottom_line : g_display.nlines,
  512.              dir3 );
  513.       rc = ERROR;
  514.    }
  515.    return( rc );
  516. }
  517.  
  518.  
  519. /*
  520.  * Name:    setup_directory_window
  521.  * Purpose: set number of rows and cols in directory window
  522.  * Date:    February 13, 1992
  523.  * Passed:  dir: pointer to directory structure
  524.  *          cnt: number of files
  525.  * Notes:   set up stuff we need to know about how to display files.
  526.  */
  527. void setup_directory_window( DIRECTORY *dir, int cnt )
  528. {
  529. int  i;
  530. int  wid;
  531. char temp[MAX_COLS];    /* line to output */
  532.  
  533.    /*
  534.     * setup the fixed vars used in dir display.
  535.     *    dir->col =      physical upper left column of dir screen
  536.     *    dir->row =      physical upper left row or line of dir screen
  537.     *    dir->wid =      width of physical screen
  538.     *    dir->hgt =      height of physical screen
  539.     *    dir->max_cols   number of columns of files in dir screen
  540.     *    dir->max_lines  number of lines of files in each column in dir screen
  541.     *    dir->cnt        number of files in list
  542.     */
  543.    dir->col = 3;
  544.    dir->row = 5;
  545.    wid = dir->wid = 72;
  546.    dir->hgt = 16;
  547.    dir->max_cols = 5;
  548.    dir->max_lines = 9;
  549.    dir->cnt = cnt;
  550.  
  551.    /*
  552.     * Find out how many lines in each column are needed to display
  553.     *  matching files.
  554.     */
  555.    dir->lines = dir->cnt / dir->max_cols + (dir->cnt % dir->max_cols ? 1 : 0);
  556.    if (dir->lines > dir->max_lines)
  557.       dir->lines = dir->max_lines;
  558.  
  559.    /*
  560.     * Find out how many columns of file names we need.
  561.     */
  562.    dir->cols = dir->cnt / dir->lines + (dir->cnt % dir->lines ? 1 : 0);
  563.    if (dir->cols > dir->max_cols)
  564.       dir->cols = dir->max_cols;
  565.  
  566.  
  567.    /*
  568.     * Find the maximun number of file names we can display in help screen.
  569.     */
  570.    dir->avail = dir->lines * dir->cols;
  571.  
  572.    /*
  573.     * Now find the number of file names we do have on the screen.  Every
  574.     *  time we slide the "window", we have to calculate a new nfiles.
  575.     */
  576.    dir->nfiles = dir->cnt > dir->avail ? dir->avail : dir->cnt;
  577.  
  578.    /*
  579.     * A lot of times, the number of matching files will not fit evenly
  580.     *  in our help screen.  The last column on the right will be partially
  581.     *  filled, hence the variable name prow (partial row).  When there are
  582.     *  more file names than can fit on the screen, we have to calculate
  583.     *  prow every time we slide the "window" of files.
  584.     */
  585.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  586.  
  587.    /*
  588.     * Find out how many "virtual" columns of file names we have.  If
  589.     *  all the files can fit in the dir screen, there will be no
  590.     *  virtual columns.
  591.     */
  592.    if (dir->cnt < dir->avail)
  593.       dir->vcols = 0;
  594.    else
  595.       dir->vcols =  (dir->cnt - dir->avail) / dir->max_lines +
  596.                    ((dir->cnt - dir->avail) % dir->max_lines ? 1 : 0);
  597.  
  598.    /*
  599.     * Find the physical display column in dir screen.
  600.     */
  601.    dir->flist_col[0] = dir->col + 2;
  602.    for (i=1; i<dir->max_cols; i++)
  603.       dir->flist_col[i] = dir->flist_col[i-1] + 14;
  604.  
  605.    /*
  606.     * Now, draw the borders of the dir screen.
  607.     */
  608.    for (i=0; i < dir->hgt; i++) {
  609.       if (i == 0 || i == dir->hgt-1) {
  610.          memset( temp, '─', wid );
  611.          temp[wid] = '\0';
  612.          if (i == 0) {
  613.             temp[0] = '┌';
  614.             temp[wid-1] = '┐';
  615.          } else {
  616.             temp[0] = '└';
  617.             temp[wid-1] = '┘';
  618.          }
  619.       } else {
  620.          memset( temp, ' ', wid );
  621.          temp[wid] = '\0';
  622.          temp[0] = temp[wid-1] = '│';
  623.       }
  624.       s_output( temp, dir->row+i, dir->col, g_display.help_color );
  625.    }
  626.  
  627.    /*
  628.     * Write headings in help screen.
  629.     */
  630.    s_output( dir4, dir->row+1, dir->col+3, g_display.help_color );
  631.    s_output( dir5, dir->row+2, dir->col+3, g_display.help_color );
  632.    s_output( dir6, dir->row+2, dir->col+44, g_display.help_color );
  633.    s_output( dir7, dir->row+14, dir->col+8, g_display.help_color );
  634. }
  635.  
  636.  
  637. /*
  638.  * Name:    write_directory_list
  639.  * Purpose: given directory list, display matching files
  640.  * Date:    February 13, 1992
  641.  * Passed:  flist: pointer to list of files
  642.  *          dir:   directory display structure
  643.  * Notes:   blank out the previous file name and display the new one.
  644.  */
  645. void write_directory_list( FTYPE *flist, DIRECTORY dir )
  646. {
  647. FTYPE *p, *top;
  648. int  i;
  649. int  j;
  650. int  k;
  651. int  end;
  652. int  line;
  653. int  col;
  654. int  color;
  655.  
  656.    color = g_display.help_color;
  657.    top = flist;
  658.    for (i=0; i < dir.lines; ++i) {
  659.       p = top;
  660.       end = FALSE;
  661.       for (j=0; j < dir.cols; ++j) {
  662.          col = dir.flist_col[j];
  663.          line = i + dir.row + 4;
  664.  
  665.          /*
  666.           * We need to blank out all lines and columns used to display
  667.           *  files, because there may be some residue from a previous dir
  668.           */
  669.          s_output( "            ", line, col, color );
  670.          if (!end) {
  671.             s_output( p->fname, line, col, color );
  672.             p += dir.lines;
  673.             k = p - flist;
  674.             if (k >= dir.nfiles)
  675.                end = TRUE;
  676.          }
  677.       }
  678.       ++top;
  679.    }
  680. }
  681.  
  682.  
  683. /*
  684.  * Name:    select_file
  685.  * Purpose: To let user select a file from dir list
  686.  * Date:    February 13, 1992
  687.  * Passed:  flist: pointer to list of files
  688.  *          stem:  base directory
  689.  *          dir:   directory display stuff
  690.  * Notes:   let user move thru the file names with the cursor keys
  691.  */
  692. int  select_file( FTYPE *flist, char *stem, DIRECTORY *dir )
  693. {
  694. int  ch;                /* input character from user */
  695. int  func;              /* function of character input by user */
  696. int  fno;               /* index into flist of the file under cursor */
  697. int  goodkey;           /* is key a recognized function key? */
  698. int  r;                 /* current row of cursor */
  699. int  c;                 /* current column of cursor */
  700. int  offset;            /* offset into file list */
  701. int  stop;              /* stop indicator */
  702. int  stem_len;          /* stem length */
  703. int  color;             /* color of help screen */
  704. int  file_color;        /* color of current file */
  705. int  change;            /* boolean, hilite another file? */
  706. int  oldr;              /* old row */
  707. int  oldc;              /* old column */
  708. char asize[20];         /* ascii file size */
  709. char blank[20];         /* blank out file names */
  710.  
  711.    /*
  712.     * initial everything.
  713.     */
  714.    memset( blank, ' ', 12 );
  715.    blank[12] = '\0';
  716.    c = r = 1;
  717.    ch = fno = offset = 0;
  718.    color = g_display.help_color;
  719.    file_color = g_display.hilited_file;
  720.    goodkey = TRUE;
  721.    stop = FALSE;
  722.    stem_len = strlen( stem );
  723.    s_output( stem, dir->row+1, dir->col+19, color );
  724.    s_output( flist[fno].fname, dir->row+1, dir->col+19+stem_len, color );
  725.    ltoa( flist[fno].fsize, asize, 10 );
  726.    s_output( blank, dir->row+2, dir->col+19, color );
  727.    s_output( asize, dir->row+2, dir->col+19, color );
  728.    itoa( dir->cnt,  asize, 10 );
  729.    s_output( blank, dir->row+2, dir->col+57, color );
  730.    s_output( asize, dir->row+2, dir->col+57, color );
  731.    xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  732.    hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  733.    change = FALSE;
  734.    while (stop == FALSE) {
  735.       oldr = r;
  736.       oldc = c;
  737.       ch = getkey( );
  738.       func = getfunc( ch );
  739.  
  740.       /*
  741.        * User may have redefined the Enter and ESC keys.  Make the Enter key
  742.        *  perform a Rturn in this function. Make the ESC key do an AbortCommand.
  743.        */
  744.       if (ch == RTURN)
  745.          func = Rturn;
  746.       else if (ch == ESC)
  747.          func = AbortCommand;
  748.  
  749.       switch (func) {
  750.          case Rturn       :
  751.          case NextLine    :
  752.          case BegNextLine :
  753.             stop = TRUE;
  754.             break;
  755.          case AbortCommand :
  756.             stop = TRUE;
  757.             break;
  758.          case LineUp :
  759.             if (r > 1) {
  760.                change = TRUE;
  761.                --r;
  762.             } else {
  763.                r = dir->lines;
  764.                change = TRUE;
  765.                if (offset == 0 || c > 1) {
  766.                   if (c > 1)
  767.                      --c;
  768.                } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  769.                   /*
  770.                    * recalculate the dir display stuff.
  771.                    */
  772.                   offset -= dir->lines;
  773.                   recalculate_dir( dir, flist, offset );
  774.                }
  775.             }
  776.             goodkey = TRUE;
  777.             break;
  778.          case LineDown :
  779.             if (r < dir->prow) {
  780.                change = TRUE;
  781.                ++r;
  782.             } else if (r < dir->lines && c != dir->cols) {
  783.                change = TRUE;
  784.                ++r;
  785.             } else {
  786.                change = TRUE;
  787.                r = 1;
  788.                if (offset == dir->vcols * dir->lines || c < dir->cols) {
  789.                   if (c < dir->cols)
  790.                      ++c;
  791.                } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  792.                          c == dir->cols) {
  793.                   offset += dir->lines;
  794.                   recalculate_dir( dir, flist, offset );
  795.                }
  796.             }
  797.             goodkey = TRUE;
  798.             break;
  799.          case CharLeft :
  800.             if (offset == 0 || c > 1) {
  801.                if (c > 1) {
  802.                   change = TRUE;
  803.                   --c;
  804.                }
  805.             } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  806.                change = TRUE;
  807.  
  808.                /*
  809.                 * recalculate the dir display stuff.
  810.                 */
  811.                offset -= dir->lines;
  812.                recalculate_dir( dir, flist, offset );
  813.             }
  814.             goodkey = TRUE;
  815.             break;
  816.          case CharRight :
  817.             if (offset == dir->vcols * dir->lines || c < dir->cols) {
  818.                if (c < dir->cols) {
  819.                   change = TRUE;
  820.                   ++c;
  821.                   if (c == dir->cols) {
  822.                      if ( r > dir->prow)
  823.                         r = dir->prow;
  824.                   }
  825.                }
  826.             } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  827.                          c == dir->cols) {
  828.                change = TRUE;
  829.                offset += dir->lines;
  830.                recalculate_dir( dir, flist, offset );
  831.                if (r > dir->prow)
  832.                   r = dir->prow;
  833.             }
  834.             goodkey = TRUE;
  835.             break;
  836.          case BegOfLine :
  837.             change = TRUE;
  838.             c = r = 1;
  839.             goodkey = TRUE;
  840.             break;
  841.          case EndOfLine :
  842.             change = TRUE;
  843.             r = dir->prow;
  844.             c = dir->cols;
  845.             goodkey = TRUE;
  846.             break;
  847.          case ScreenDown :
  848.             change = TRUE;
  849.             r = (c == dir->cols) ? r = dir->prow : dir->lines;
  850.             goodkey = TRUE;
  851.             break;
  852.          case ScreenUp :
  853.             change = TRUE;
  854.             r = 1;
  855.             goodkey = TRUE;
  856.             break;
  857.          default :
  858.             break;
  859.       }
  860.       if (goodkey) {
  861.          s_output( blank, dir->row+1, dir->col+19+stem_len, color );
  862.          fno = offset + (c-1)*dir->lines + (r-1);
  863.          s_output( flist[fno].fname, dir->row+1, dir->col+19+stem_len, color );
  864.          ltoa( flist[fno].fsize, asize, 10 );
  865.          s_output( blank, dir->row+2, dir->col+19, color );
  866.          s_output( asize, dir->row+2, dir->col+19, color );
  867.          xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  868.          goodkey = FALSE;
  869.          if (change) {
  870.             hlight_line( (oldc-1)*14+dir->col+2, oldr+dir->row+3, 12, color );
  871.             hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  872.             change = FALSE;
  873.          }
  874.       }
  875.    }
  876.    dir->select = fno;
  877.    return( func == AbortCommand ? ERROR : OK );
  878. }
  879.  
  880.  
  881. /*
  882.  * Name:    recalculate_dir
  883.  * Purpose: To recalcute dir structure when cursor goes ahead or behind screen
  884.  * Date:    February 13, 1992
  885.  * Passed:  dir:    pointer to file structure
  886.  *          flist:  pointer to file structure
  887.  *          offset: number of files from beginning of flist
  888.  * Notes:   Find new number of files on the screen.  Then, find out
  889.  *           how many files names are in the last column.
  890.  */
  891. void recalculate_dir( DIRECTORY *dir , FTYPE *flist, int offset )
  892. {
  893. register int off;
  894.  
  895.    off = offset;
  896.    dir->nfiles = (dir->cnt - off) > dir->avail ? dir->avail :
  897.                 (dir->cnt - off);
  898.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  899.    write_directory_list( flist+off, *dir );
  900. }
  901.  
  902.  
  903. /*
  904.  * Name:    shell_sort
  905.  * Purpose: To sort file names
  906.  * Date:    February 13, 1992
  907.  * Passed:  flist: pointer to file structure
  908.  *          cnt:   number of files to sort
  909.  * Notes:   this implementation of Shellsort is based on the one by Robert
  910.  *           Sedgewick on page 109, _Algorithms in C_.
  911.  */
  912. void shell_sort( FTYPE *flist, int cnt )
  913. {
  914. int  i;
  915. register int j;
  916. register int inc;
  917. FTYPE temp;
  918. FTYPE *fl;
  919.  
  920.    if (cnt > 1) {
  921.       fl = flist;
  922.  
  923.       /*
  924.        * figure the increments, per Donald Knuth, _Sorting and Searching_, and
  925.        *  Robert Sedgewick, _Algorithms in C_.
  926.        */
  927.       j = cnt / 9;
  928.       for (inc=1; inc <= j; inc = 3 * inc + 1);
  929.  
  930.       /*
  931.        * now, Shellsort the directory file names.
  932.        */
  933.       for (; inc > 0; inc /= 3) {
  934.          for (i=inc; i < cnt; i++) {
  935.             j = i;
  936.             memcpy( &temp, fl+j, sizeof(FTYPE) );
  937.             while (j >= inc && memcmp( fl[j-inc].fname, temp.fname, 14 ) > 0) {
  938.                memcpy( fl+j, fl+j-inc, sizeof(FTYPE) );
  939.                j -= inc;
  940.             }
  941.             memcpy( fl+j, &temp, sizeof(FTYPE) );
  942.          }
  943.       }
  944.    }
  945. }
  946.